#!/usr/bin/perl -w # useful for syntax highlighting

package clreqd_modules;

use Fcntl;
use clutils;
use cldb;
use clstatdb;
use Sys::Hostname;
use Data::Dumper;

# The purpose of this request is to look at all the validated disk
# heartbeats configured and load in all records for those.
# It then reads the blocks necessary from each offset for each active
# host in the cluster. It will return the update times for each
# host/diskheart in the following format:
#
# hb node uptimedate interval
# 
# The first line is "OK" or "ERROR MSG"
#

sub diskhb_writetimes {
my %ARGS=@_;

	my $hostname=hostname;
	# Open the configuration database and read in all details
	# of validated disk entries...
	my $db=new cldb("$::ENV{TRUECL_CFG}/config.db");
	if(!defined($db)) {
		return "ERROR Unable to open configuration database.";
	}

	my $h=$db->get_rows("SELECT value FROM globals WHERE var = 'interval';");
	my $interval;
	if(!defined($h->[0]->{value})) {
		my $interval=2;
	} else {
		$interval=int($h->[0]->{value});
	}
	$h=$db->get_rows("SELECT * FROM diskhb WHERE validated = '1';");
	$db->disconnect;

	#################################################################
	# Define the list of validated heartbeat names...		#
	#################################################################

	my @HBS=();
	for my $crow (@$h) {
		if(defined($crow->{write_offset})) {
			if(! grep {$_ eq $crow->{name}} @HBS) {
				push @HBS,$crow->{name};
			}
		}
	}

	#################################################################
	# We need to work out which hosts are currently active in the	#
	# cluster so we can ignore all the others...			#
	#################################################################

	my @HOSTS=();
	{
		my $db=new clstatdb("$::ENV{TRUECL_CFG}/status.db");
		if(!defined($db)) {
			return ("ERROR Unable to open status database");
		}
		my $h=$db->get_rows("SELECT * FROM cluster_status_vars WHERE var LIKE '%:clusternodestate' AND value = 'Joined';");

		if(!defined($h) || !defined($h->[0]->{var})) {
			return "ERROR No results from cluster_status_vars query";
		}
		for my $crec (@$h) {
			my $x=$crec->{var};
			$x =~ s/:clusternodestate$//;
			push @HOSTS,$x;
		}
	}
	my %first_read=();

	# Ok - we need to read details from each heartbeat device using the local
	# copy...

	for my $chb (@HBS) {
		$first_read{$chb}={};
		my $dev=undef;
		my $len=undef;
		for my $crow (@$h) {
			if($crow->{node} eq $hostname && $crow->{name} eq $chb) {
				$dev=$crow->{devpath};
				$len=int($crow->{blksize});
				last;
			}
		}
		if(!defined($dev)) {
			::warning("Now device found for heartbeat '$chb' on host '$hostname' - ignoring from heartbeat checks.");
			next;
		}
		#################################################################
		# We have the local raw device details for this heartbeat so	#
		# we get the latest update times for all the alive hosts.	#
		#################################################################
		for my $crow (@$h) {
			next if ! grep {$crow->{node} eq $_} @HOSTS;
			if($crow->{name} eq $chb) {
				my $offset=int($crow->{write_offset});
				my $data=_diskhb_writetimes_read($dev,$offset,$len);
				if($data =~ /^ERROR/) {
					$data =~ s/^ERROR\s+//;
					$first_read{$chb}->{$crow->{node}}={
						content => $data,
					};
					next;
				}
				$data =~ s/^OK\s+//;
				my $ut=undef;
				if($data =~ /^CTIME\s+(\d+)/) {
					$ut=$1;
					$first_read{$chb}->{$crow->{node}}={
						uptime => int($ut),
					};
				} else {
					$first_read{$chb}->{$crow->{node}}={
						content => "None standard content: " . substr($data,0,25),
					};
				}
			}
		}
	}

	# So return the information as required...
	# Wait a little while before repeating the process...

	my @R=();
	for my $chb (sort keys %first_read) {
		for my $cnode (sort keys %{$first_read{$chb}}) {
			if(!exists($first_read{$chb}->{$cnode}->{uptime})) {
				push @R,"$chb $cnode ERROR $first_read{$chb}->{$cnode}->{content}";
			} else {
				push @R,"$chb $cnode $first_read{$chb}->{$cnode}->{uptime} $interval";
			}
		}
	}
	return "OK\n" . join("\n",@R);
}

sub _diskhb_writetimes_read($$$) {
my $fd;
my $devname=shift;
my $offset=shift;
my $length=shift;

	sysopen($fd,$devname,O_RDWR);
	if(! $fd) {
		return "ERROR Unable to open device '$devname': $!";
	}
	if(sysseek($fd,$offset,0) != $offset) {
		return "ERROR Unable to seek to offset '$offset' on '$devname'.";
	}
	my $d;
	if(sysread($fd,$d,$length) != $length) {
		return "ERROR Unable to read '$length' bytes of data from '$devname'.";
	}
	close($fd);
	return "OK $d";
	# return "OK " . unpack("H*",$d);
}

1;
